<?php
if(!defined('TABLE_SETCOM')){
define('TABLE_SETCOM' , 'setcom');
}

class setcom {
	var $code, $title, $description, $enabled;
	
	var $currencies = array("ZAR"=>"R","GBP"=>'?','USD'=>'$','EUR'=>'E');


	var $forceRedirect;
	
	var $email_footer = "";
	
	// class constructor
	function setcom() {
		global $order;
		
		if ( extension_loaded ( "curl" ) != true) {
			echo MODULE_PAYMENT_SETCOM_CURL_ERROR;
			return false;
		}
		
		
		$this->code = 'setcom';
		$this->title = MODULE_PAYMENT_SETCOM_TEXT_TITLE;
		$this->description = MODULE_PAYMENT_SETCOM_TEXT_DESCRIPTION;
		$this->sort_order = MODULE_PAYMENT_SETCOM_SORT_ORDER;
		$this->enabled = ((MODULE_PAYMENT_SETCOM_STATUS == 'True') ? true : false);
		
		if (( int ) MODULE_PAYMENT_SETCOM_ORDER_STATUS_ID > 0) {
			$this->order_status = MODULE_PAYMENT_SETCOM_ORDER_STATUS_ID;
		}
		
		if (is_object ( $order ))
			$this->update_status ();
		
		$this->form_action_url = 'https://www.setcom.com/secure/';
	}
	
	/**
	 * Controls whether Setcom payment module is enabled or disabled
	 *
	 */
    function update_status() {
      global $order;

      if ( ($this->enabled == true) && ((int)MODULE_PAYMENT_SETCOM_ZONE > 0) ) {
        $check_flag = false;
        $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . MODULE_PAYMENT_SETCOM_ZONE . "' and zone_country_id = '" . $order->billing['country']['id'] . "' order by zone_id");
        while ($check = tep_db_fetch_array($check_query)) {
          if ($check['zone_id'] < 1) {
            $check_flag = true;
            break;
          } elseif ($check['zone_id'] == $order->billing['zone_id']) {
            $check_flag = true;
            break;
          }
        }

        if ($check_flag == false) {
          $this->enabled = false;
        }
      }
   }
	
	function javascript_validation() {
		return false;
	}
	
	function selection() { // from Paypal_IPN
		$img_setcom = DIR_WS_MODULES . 'payment/setcom/images/setcom.gif';
		$img_visa = DIR_WS_MODULES . 'payment/setcom/images/visa.gif';
		$img_mc = DIR_WS_MODULES . 'payment/setcom/images/mastercard.gif';
		$set_cc_txt = sprintf ( MODULE_PAYMENT_SETCOM_CC_TEXT, tep_image ( $img_setcom, ' Setcom ', '', '', 'align=ABSMIDDLE' ), tep_image ( $img_visa, ' Visa ', '', '', 'align=ABSMIDDLE' ), tep_image ( $img_mc, ' MasterCard ', '', '', 'align=ABSMIDDLE' ) );
		$fields [] = array ('title' => '', //MODULE_PAYMENT_SETCOM_TEXT_TITLE,
'field' => '<div><b>' . $set_cc_txt . '</b></div>' );
		return array ('id' => $this->code, 'module' => $this->title, 'fields' => $fields );
	}
	
	function pre_confirmation_check() {
		return false;
	}
	
	function confirmation() {
		$title = $this->title;
		$confirmation = array ('title' => MODULE_PAYMENT_SETCOM_TEXT_PAY_INFO );
		
		return $confirmation;
	}
	
	function process_button() {
		global $customer_id, $order, $i, $n, $j, $languages_id;
		

			
			// Merchant Reference that's passed back from Setcom to Oscommerce as order reference:- Customer Database ID + Email + Date & Time
			$MCustom = ('CID: ' . $customer_id . ' - ' . $order->customer ['email_address'] . ' - ' . date ( "Y.m.d H:i:s" ));
			
			// The process_button_string contains the form that is sent to Setcom.
			$process_button_string = '';
			$tprice = 0; //we need this to cross check for gift vouchers / points / coupons
			$process_button_string .= tep_draw_hidden_field ( 'ButtonAction', 'checkout' ) . tep_draw_hidden_field ( 'MerchantIdentifier', MODULE_PAYMENT_SETCOM_MERCHANTID ) . tep_draw_hidden_field ( 'CurrencyAlphaCode', strtoupper ( $order->info ['currency'] ) ) . tep_draw_hidden_field ( 'MerchCustom', $MCustom ) . tep_draw_hidden_field ( 'BuyerInformation', '1' ) . tep_draw_hidden_field ( 'Firstname', $order->customer ['firstname'] ) . tep_draw_hidden_field ( 'Lastname', $order->customer ['lastname'] ) . tep_draw_hidden_field ( 'Address1', $order->billing ['street_address'] ) . tep_draw_hidden_field ( 'City', $order->billing ['city'] ) . tep_draw_hidden_field ( 'State', $order->billing ['state'] ) . tep_draw_hidden_field ( 'PostalCode', $order->billing ['postcode'] ) . tep_draw_hidden_field ( 'Country', $order->billing ['country'] ['iso_code_2'] ) . tep_draw_hidden_field ( 'Email', $order->customer ['email_address'] ) . tep_draw_hidden_field ( 'WorkNumber', $order->customer ['telephone'] );
			
			if (! empty ( $order->billing ['suburb'] ))
				$process_button_string .= tep_draw_hidden_field ( 'Address2', $order->billing ['suburb'] );
				
			// Creates form data for each product ordered
			for($i = 0, $n = sizeof ( $order->products ); $i < $n; $i ++) {
				if ($i != 0)
					$j = $i; else
					$j = '';
				$sLidPrice = ($order->products [$i] ['final_price'] * (($order->products [$i] ['tax'] / 100 + 1))) * $order->info ['currency_value'];
				$process_button_string .= tep_draw_hidden_field ( 'LIDSKU' . $j, $order->products [$i] ['id'] ) . tep_draw_hidden_field ( 'LIDDesc' . $j, $order->products [$i] ['name'] ) . tep_draw_hidden_field ( 'LIDPrice' . $j, number_format ( $sLidPrice, 2, '.', '' ) ) . tep_draw_hidden_field ( 'LIDQty' . $j, $order->products [$i] ['qty'] ) . tep_draw_hidden_field ( 'ShippingRequired' . $j, '0' ) . tep_draw_hidden_field ( 'IsVoucher' . $j, '0' );
				
				$tprice+=($sLidPrice * $order->products [$i] ['qty']);
			}
			
			$modifiers = $this->_get_all_total_modifiers ();
			foreach ( $modifiers as $modifier ) {
				$j ++;
				$process_button_string .= tep_draw_hidden_field ( 'LIDSKU' . $j, $modifier ['code'] ) . tep_draw_hidden_field ( 'LIDDesc' . $j, $modifier ['text'] ) . tep_draw_hidden_field ( 'LIDPrice' . $j, number_format ( $modifier ['value'] * $order->info ['currency_value'], 2, '.', '' ) ) . tep_draw_hidden_field ( 'LIDQty' . $j, '1' ) . tep_draw_hidden_field ( 'ShippingRequired' . $j, '0' ) . tep_draw_hidden_field ( 'IsVoucher' . $j, '0' );
				$tmprice = ($modifier ['value'] * $order->info ['currency_value']);
				$tprice+=$tmprice;
				
			}
			// Create form data for Delivery Cost (if any), delivery cost is yet another entry n the invoice
			$sDeliverCost = $order->info ['shipping_cost'] * $order->info ['currency_value'];
			if (! empty ( $sDeliverCost )) {
				$j ++;
				$process_button_string .= tep_draw_hidden_field ( 'LIDSKU' . $j, 'shipme' ) . tep_draw_hidden_field ( 'LIDDesc' . $j, 'Delivery Cost' ) . tep_draw_hidden_field ( 'LIDPrice' . $j, number_format ( $sDeliverCost, 2, '.', '' ) ) . tep_draw_hidden_field ( 'LIDQty' . $j, '1' ) . tep_draw_hidden_field ( 'ShippingRequired' . $j, '0' ) . tep_draw_hidden_field ( 'IsVoucher' . $j, '0' );
				
				$tprice+=$sDeliverCost;
			}
//echo $tprice . ' - ' . ($order->info['total'] * $order->info ['currency_value']);
$order_value = ($order->info['total'] * $order->info ['currency_value']);
 if($order_value < $tprice){
 $discount = number_format ( $order_value-$tprice, 2, '.', '' );
 //echo "<br>$discount";
 $j++;
 $process_button_string .= tep_draw_hidden_field ( 'LIDSKU' . $j, 'discou' ) . tep_draw_hidden_field ( 'LIDDesc' . $j, 'Discount' ) . tep_draw_hidden_field ( 'LIDPrice' . $j, number_format ( $discount, 2, '.', '' ) ) . tep_draw_hidden_field ( 'LIDQty' . $j, '1' ) . tep_draw_hidden_field ( 'ShippingRequired' . $j, '0' ) . tep_draw_hidden_field ( 'IsVoucher' . $j, '0' );
 }
		return $process_button_string;
	}
	


	function before_process($ignore_redirect = false,$payment_data = false,$order_id='',$setcomid=null) {
		global $messageStack;

		if(is_array($payment_data)) {
			$sTnxid = $payment_data['tnxid'];
			$sChecksum = $payment_data['checksum'];
			$sParity = $payment_data['parity'];
			$action = "update";
			$parameters = "`s_setcom_id`='$setcomid'";
		} else {
			$sTnxid = trim($_GET ['tnxid']);
			$sChecksum = trim($_GET ['checksum']);
			$sParity = trim($_GET ['parity']);
			$action = "insert";
			$parameters='';
		}

		//Here we contact Setcom and hope for some not false result, if it's false, we have nothing more to do
		$communication_result = $this->order_synchro ( $sTnxid, $sChecksum, $sParity );
		if (! $communication_result) {
			$this->_add_error( MODULE_PAYMENT_SETCOM_COMMUNICATION_ERROR, 'error' );
			$this->forceRedirect = tep_href_link ( FILENAME_CHECKOUT_PAYMENT, 'SSL', true, false ); 
		}
		//Proccess the response into object		
		$order_synchro = $this->_proccess_response ( $communication_result );

		$sStatus = (string)$order_synchro->outcome->status;
		$sPType = (string)$order_synchro->payment_instrument->type;
		$sErrorCode = (string)$order_synchro->outcome->error_code;
		$sErrorDesc = $order_synchro->outcome->error_desc;
		$sErrorSolution = $order_synchro->outcome->error_solution;
		
		//If error received, display them and redirect user
		if (
		  (!empty($sErrorCode) && $sErrorCode != "0") || 				//error code different then 0 means trouble
		  ($sStatus != "Complete" && $sPType != "eDeposit") ||			//for everything else then eDeposit, we expect status to be "Complete"
		  ($sStatus != "Pending" && $sPType == "eDeposit"))				//expected status for eDespoist payment type is "Pending"
		 {
			if ($sStatus != "Complete") {
				$this->_add_error( MODULE_PAYMENT_SETCOM_TEXT_DECLINED_MESSAGE, 'error');
			}
			if (! empty ( $sErrorDesc )) {
				$this->_add_error( $sErrorDesc, 'error' );
			}
			if (! empty ( $sErrorSolution )) {
				$this->_add_error( $sErrorSolution, 'error' );
			}
			$this->_add_error( MODULE_PAYMENT_SETCOM_PAYMENT_DONT_MATCH_ORDER, 'warning' );
			$this->forceRedirect = tep_href_link ( FILENAME_CHECKOUT_PAYMENT, 'SSL', true, false ); 
		}

		//if there are any alerts, we need to let the user know about it
		if(isset($order_synchro->outcome->order->alerts)) {
			$aOrderAlertsAttributes = $order_synchro->outcome->order->alerts->attributes ();
			if ($aOrderAlertsAttributes ['count'] > 0) {
				$aOrderAlerts = array ( );
				foreach ( $order_synchro->outcome->order->alerts->children() as $alert ) {
					if(strlen((string)$alert->text)>0)
						$this->_add_error((string)$alert->text, 'caution' );
				}
			}
		}
		// we need to be sure, that what comes from Setcom, acctualy matches what is real on our server
		if(isset($order_synchro->seller) && isset($order_synchro->financial) && isset($order_synchro->buyer)) {
			$sMUserName = (string)$order_synchro->seller->username;
			$sIdenti = (string)$order_synchro->seller->identifier;
			if ($sIdenti != MODULE_PAYMENT_SETCOM_MERCHANTID || $sMUserName != MODULE_PAYMENT_SETCOM_USERNAME) {
				$this->_add_error( MODULE_PAYMENT_SETCOM_SELLER_DONT_MATCH ,'error');
				$this->forceRedirect = tep_href_link ( FILENAME_CHECKOUT_PAYMENT, 'SSL', true, false );
			}
			/*
			//ok this is good, however we need to get some checking vs discount!!!
			//not good if using gift vouchers, points etc
			$sFAmount = (int)$order_synchro->financial->amount_total;
			global $order;
			if (isset($order) && $order->info ['total'] != ($sFAmount/100)) {
			//ok halt, we have an issue here
			print_r($order);
			die();
				$this->_add_error( MODULE_PAYMENT_SETCOM_TOTAL_DONT_MATCH, 'error');
				$this->forceRedirect = tep_href_link ( FILENAME_CHECKOUT_PAYMENT, 'SSL', true, false );
			}
			*/
			
			$sCurrency = (string)$order_synchro->financial->currency;
			if (isset($GLOBALS ['order']) && $GLOBALS ['order']->info ['currency'] != $sCurrency) {
				$this->_add_error(MODULE_PAYMENT_SETCOM_CURRENCY_DONT_MATCH, 'error' );
				$this->forceRedirect = tep_href_link ( FILENAME_CHECKOUT_PAYMENT, 'SSL', true, false );
			}
		
			//this combined value wil be easier to add to db
			$sBillingDetails = 	$order_synchro->buyer->billing_address->title .' ' .
								$order_synchro->buyer->billing_address->firstname .' ' .
								$order_synchro->buyer->billing_address->firstname .', ' .
								$order_synchro->buyer->billing_address->email_address .', ' .
								$order_synchro->buyer->billing_address->street1 .', ' .
								$order_synchro->buyer->billing_address->street2 .', ' .
								$order_synchro->buyer->billing_address->city .', ' .
								$order_synchro->buyer->billing_address->state .', ' .
								$order_synchro->buyer->billing_address->zip .', ' .
								$order_synchro->buyer->billing_address->country .', ' .
								$order_synchro->buyer->billing_address->contact_number;

		}
		//Insert data into setcom table in database
		$errors=  $this->_render_errors();
		$this->email_footer = strip_tags(str_replace("<li>","\n",(str_replace("</tr>","\n",$errors))));
		
		$set_sql_data_array = array (
			's_setcom_id' => $setcomid,
			's_order_id' => $order_id,	//this is set in after_process()
			's_customers_id' => $_SESSION['customer_id'], 
			's_date_today' => date ( "Y-m-d H:i:s" ), 
			's_status' => $sStatus, 
			's_otransid' => (string)$order_synchro->outcome->order->id, //this is changed, now it stores Setcom's order id
			's_identi' => $sIdenti, 
			's_musername' => $sMUserName, 
			's_mreference' => (string)$order_synchro->seller->reference,
		 	's_busername' => (string)$order_synchro->buyer->username, 
		 	's_bemailadd' => $sBillingDetails, 
		 	's_ptype' => $sPType, 

		 	's_famount' => $this->_format_amount($sFAmount,$sCurrency), 
		 	's_currency' => $sCurrency, 
		 	's_dateadded' => date ( "Y-m-d H:i:s" ), 
		 	's_sreference' => $order_synchro->buyer->reference, 
		 	's_errorcode' => $sErrorCode, 

		 	's_samount' => $this->_format_amount((string)$order_synchro->financial->amount_sub_total,$sCurrency), 
		 	's_fee' => '',	//Setcome does not return such value, check order detail for this information 
		 	's_tax' => $this->_format_amount((string)$order_synchro->financial->amount_tax,$sCurrency),  

		 	's_fundsavail' => (string) $order_synchro->outcome->order->funds_available, 
		 	's_rollreserveavail' => '', //this is no longer present
		 	's_curlerror' => $errors, //if curl returns an error, we never get to this place, proccessing errors are here now
		 	'tnxid'=>$sTnxid,
			'checksum'=>$sChecksum,
			'parity'=>$sParity,
		);
		//die($sStatus);
		//the extra params are define at top of the function, used for update
		tep_db_perform ( TABLE_SETCOM, $set_sql_data_array,$action,$parameters );
//die($sStatus);
		//if there are errors, we should redirect user
		if(strlen($this->forceRedirect)>0 && $ignore_redirect != true ) tep_redirect($this->forceRedirect);
		
	}
	
	function after_process() {
		global $insert_id, $db, $messageStack;
		// Update setcom table with ZenCart order no.
		tep_db_query( "update " . TABLE_SETCOM . " set s_order_id = " . $insert_id . " where s_customers_id = " . $_SESSION['customer_id'] . " order by s_setcom_id desc limit 1" );
	}
	
	function get_error() {
		return false;
	}
	
	 function check() {
      if (!isset($this->_check)) {
        $check_query = tep_db_query("select configuration_value from " . TABLE_CONFIGURATION . " where configuration_key = 'MODULE_PAYMENT_SETCOM_STATUS'");
        $this->check = tep_db_num_rows($check_query);
      }
      return $this->check;
    
    }
	
	function install() {
		global $db;
		tep_db_query( "insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Enable Setcom Module', 'MODULE_PAYMENT_SETCOM_STATUS', 'True', 'Do you want to accept Setcom payments?', '6', '1', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())" );
		tep_db_query( "insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Merchant Identifier', 'MODULE_PAYMENT_SETCOM_MERCHANTID', '', 'Your Merchant ID', '6', '2', now())" );
		tep_db_query( "insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Merchant Username:', 'MODULE_PAYMENT_SETCOM_USERNAME', '', 'The e-mail address used to log into your Setcom Account', '6', '3', now());" );
		tep_db_query( "insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Merchant Password:', 'MODULE_PAYMENT_SETCOM_PASSWORD', '', 'The password used to log into your Setcom Account', '6', '4', now());" );
		tep_db_query( "insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Sort order of display.', 'MODULE_PAYMENT_SETCOM_SORT_ORDER', '0', 'Sort order of display. Lowest is displayed first.', '6', '6', now())" );
		tep_db_query( "insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('Payment Zone', 'MODULE_PAYMENT_SETCOM_ZONE', '0', 'If a zone is selected, only enable this payment method for that zone.', '6', '7', 'tep_get_zone_class_title', 'tep_cfg_pull_down_zone_classes(', now())" );
		tep_db_query( "insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('Set Order Status', 'MODULE_PAYMENT_SETCOM_ORDER_STATUS_ID', '0', 'Set the status of orders made with this payment module to this value', '6', '8', 'tep_cfg_pull_down_order_statuses(', 'tep_get_order_status_name', now())" );
		
		//lets add the db table if it does not exists
		
		
		tep_db_query("CREATE TABLE if not EXISTS ". TABLE_SETCOM ." (
  s_setcom_id int(11) NOT NULL auto_increment,
  s_order_id int(11) default NULL,
  s_customers_id int(11) default NULL,
  s_date_today varchar(255) default NULL,
  s_status varchar(255) default NULL,
  s_otransid varchar(255) default NULL,
  s_identi varchar(255) default NULL,
  s_musername varchar(255) default NULL,
  s_mreference varchar(255) default NULL,
  s_busername varchar(255) default NULL,
  s_bemailadd varchar(255) default NULL,
  s_ptype varchar(255) default NULL,
  s_brand varchar(255) default NULL,
  s_cardname varchar(255) default NULL,
  s_cardnum varchar(255) default NULL,
  s_famount varchar(255) default NULL,
  s_currency varchar(255) default NULL,
  s_ttransID varchar(255) default NULL,
  s_ttype varchar(255) default NULL,
  s_sellreference varchar(255) default NULL,
  s_stype varchar(255) default NULL,
  s_dateadded varchar(255) default NULL,
  s_sreference varchar(255) default NULL,
  s_errorcode varchar(255) default NULL,
  s_authonum varchar(255) default NULL,
  s_transactionkey varchar(255) default NULL,
  s_samount varchar(255) default NULL,
  s_fee varchar(255) default NULL,
  s_tax varchar(255) default NULL,
  s_rollreserve varchar(255) default NULL,
  s_nett varchar(255) default NULL,
  s_balance varchar(255) default NULL,
  s_rollreservebal varchar(255) default NULL,
  s_fundsavail varchar(255) default NULL,
  s_rollreserveavail varchar(255) default NULL,
  s_curlerror text default NULL,
  tnxid VARCHAR( 36 ) NULL ,
  checksum VARCHAR( 32 ) NULL ,
  parity VARCHAR( 10 ) NULL,
  PRIMARY KEY  (s_setcom_id)
)");
		
	}
	
	function remove() {
		global $db;
		tep_db_query( "delete from " . TABLE_CONFIGURATION . " where configuration_key in ('" . implode ( "', '", $this->keys () ) . "')" );
	}
	
	function keys() {
		return array ('MODULE_PAYMENT_SETCOM_STATUS', 'MODULE_PAYMENT_SETCOM_MERCHANTID', 'MODULE_PAYMENT_SETCOM_USERNAME', 'MODULE_PAYMENT_SETCOM_PASSWORD',  'MODULE_PAYMENT_SETCOM_SORT_ORDER', 'MODULE_PAYMENT_SETCOM_ZONE', 'MODULE_PAYMENT_SETCOM_ORDER_STATUS_ID' );
	}
	

	
	/**
	 * Returns all 'total amount' modifiers as an associative array
	 *
	 * @return array
	 */
	function _get_all_total_modifiers() {
		global $order_total_modules, $order_total, $order;
		
		$all = array ( );
		$net_change = 0;
		// lets get in hand list of order total modules
		//$modules = $GLOBALS ['order_total_modules']->modules;
		$modules = $order;
		// and now, lets get throu all of them
		foreach ( $modules as $module ) {
			// we need object, not file name, so lets fix it
			$module = $GLOBALS [substr ( $module, 0, - 4 )];
			
			//if it's not object, somehting is wrong and we do not work further with it
			if (! is_object ( $module ))
				continue;
				
			// those 3 order total modules do not modify the total, they simply state it or change other things (shipping is taken care of elsewhere), so we skip them
			if (! (is_a ( $module, 'ot_total' ) || is_a ( $module, 'ot_subtotal' ) || is_a ( $module, 'ot_shipping' ))) {
				
				//each module can have several values, those are stored in 'ouput' array, so we iterate throu whole array
				foreach ( $module->output as $key => $position ) {
					//make sure the array contains what we expect
					if (is_array ( $position ) && array_key_exists ( 'value', $position ) && array_key_exists ( 'title', $position )) {
						//we need some kind of name for product code (sku), lets make one
						$values ['code'] = $module->code . $key;
						//in some cases, value shuld be negative (ie discounts), we need to make sure this happens
						if (substr ( $position ['text'], 0, 1 ) == "-")
							$values ['value'] = - $position ['value']; else
							$values ['value'] = $position ['value'];
							//title contains ending colon, we take that out and trim possible whitespace
						$values ['text'] = trim ( substr ( $position ['title'], 0, - 1 ) );
						//now add it all into final array
						$all [] = $values;
						//for further check, we keep track of the net total change
						$net_change += $values ['value'];
					}
				}
			}
		}
		//now make sure it all adds up total = subtotal + all modifiers
		if ($GLOBALS ['order']->info ['total'] == ($GLOBALS ['order']->info ['subtotal'] + $GLOBALS ['order']->info ['shipping_cost'] + $net_change)) {
			return $all;
		}
		//there is nothing to return
		return array ( );
	}
	
	/**
	 * Gets string of data and resutrns om alike object, regardles of PHP versiion installed
	 *
	 * @param string $communication_result XML response
	 */
	function _proccess_response($communication_result) {
		$setcom_wddx = trim ( $communication_result );
		$setcom_xml = wddx_deserialize ( $setcom_wddx );
		if(extension_loaded("SimpleXML")) {
			return  simplexml_load_string ( $setcom_xml );
		} elseif(extension_loaded("domxml")) {
			
			$dom = domxml_open_mem($setcom_xml);
		    $root = $dom->document_element();
			$order_synchro = new EasyDOMXMLAccess();
			//FIXME circular references, possible memmory leak, memmory is cleared only when whole request ends
		    $order_synchro->parse_node($root,$order_synchro);
			return $order_synchro;	
		}
		return false;
	}
	
	/**
	 * Contacts Setcom Order Synchro web service method and returns it's response as XML string
	 *
	 * @param string $sTnxid
	 * @param string $sChecksum
	 * @param string $sParity
	 * @return string
	 */
	function order_synchro($sTnxid, $sChecksum, $sParity) {
		$setcom_string = 'method=order_synchro&identifier=' . MODULE_PAYMENT_SETCOM_MERCHANTID . '&usrname=' . MODULE_PAYMENT_SETCOM_USERNAME . '&pwd=' . htmlencode(MODULE_PAYMENT_SETCOM_PASSWORD) . '&tnxid=' . $sTnxid . '&checksum=' . $sChecksum . '&parity=' . $sParity;
		//use this for testing:
		//$setcom_string = 'method=order_synchro&identifier=' . "1234567892" . '&usrname=' . "testseller2@setcom.com" . '&pwd=' . "testseller" . '&tnxid=' . $sTnxid . '&checksum=' . $sChecksum . '&parity=' . $sParity;
		$setcom_url = "https://www.setcom.com/secure/components/synchro.cfc?wsdl"; // Setcom Synchro url
		$curl_handle = curl_init (); // initialize curl handle
		curl_setopt ( $curl_handle, CURLOPT_URL, $setcom_url ); // set $setcom_url to post to Setcom
		curl_setopt ( $curl_handle, CURLOPT_SSL_VERIFYPEER, 0 );
		curl_setopt ( $curl_handle, CURLOPT_HEADER, 0 );
		curl_setopt ( $curl_handle, CURLOPT_RETURNTRANSFER, 1 ); // return into a variable
		curl_setopt ( $curl_handle, CURLOPT_POST, 1 ); // set POST method
		curl_setopt ( $curl_handle, CURLOPT_POSTFIELDS, $setcom_string ); // set Post variable
		$result = curl_exec ( $curl_handle ); // Perform the POST and get the data returned by Setcom.
		if (curl_errno ( $curl_handle )) {
			$sCurlError = curl_error ( $curl_handle ); // If CURL returns an error, stores it in a variable.
			global $messageStack;
			$this->_add_error( $sCurlError, 'error' );
		}
		return $result;
	}
	
	/**
	 * Reformates date, intended for internal use
	 *
	 * @param string $xDate
	 * @return string
	 */
	function _format_date($xDate) {
		if (strlen ( $xDate ) == 14) {
			$sRYear = substr ( $xDate, 0, 4 );
			$sRMonth = substr ( $xDate, 4, 2 );
			$sRDay = substr ( $xDate, 6, 2 );
			$sRHour = substr ( $xDate, 8, 2 );
			$sRMinute = substr ( $xDate, 10, 2 );
			$sRSecond = substr ( $xDate, 12, 2 );
			$sxNewDate = $sRYear . '-' . $sRMonth . '-' . $sRDay . ' ' . $sRHour . ':' . $sRMinute . ':' . $sRSecond;
		} else {
			$sxNewDate = $xDate;
		}
		return $sxNewDate;
	}
	
	/**
	 * Reformats financial values, intended for internal use
	 *
	 * @param string $amount
	 * @param string $sCurrency
	 * @return string
	 */
	function _format_amount($amount,$sCurrency) {
		if($amount < 0 ): $sign="-"; else: $sign=""; endif;
		if(array_key_exists(strtoupper($sCurrency),$this->currencies)): $curency=$this->currencies[strtoupper($sCurrency)];else: $curency="";endif;
		$amount = $sign . $sCurrency .number_format ( (abs($amount) / 100), 2, '.', '' );
		return $amount;
	}
	
	/**
	 * Rerives all payments, supports pagination (enabled by default), and filtering
	 *
	 * @param integer $page Page number
	 * @param integer $limit Number of results on page
	 * @param string $order Name of the field for SQL ORDER clausule
	 * @param string $status If set, only this status will be in result
	 * @param string $user If set, only this user will be included in result
	 * @return array
	 */
	function get_all_payments($page=1,$limit=25,$order="s_setcom_id",$status=false,$user=false) {
		global $db;
		$status_query = "";
		$date_query = "";
		$userquery="";
		$limitquery = "";
		$result = array();
		if($status) $status_query = "AND `s_status` = '$status'";
		if($user) $userquery = "AND `s_customers_id` = '$user''";
		if($limit) $limitquery = "LIMIT $limit";
		if($page && $limit) {
			$page=(int) $page;
			$start = ($page-1)*$limit;
			$limitquery = "LIMIT $start,$limit";
		}
		
		$query = "select s_order_id, s_date_today, s_status, s_otransid, s_ptype,
   		s_famount, s_currency, s_dateadded, s_fundsavail from " . TABLE_SETCOM . " WHERE 1=1 $userquery $status_query ORDER BY $order DESC $limitquery";
		$query_result = tep_db_query($query);
		while (!$query_result->EOF) {
			$result[] = $query_result->fields;
			$query_result->MoveNext();
		}
		return $result;
	}

	/**
	 * Updates fiven payment information, provided that database stores tnxi, checksum and parity for this payment
	 *
	 * @param integer $order_id
	 * @return boolean
	 */
	function update_payment($order_id) {
		global $db;
		$query = "select s_setcom_id,s_order_id,tnxid,checksum,parity FROM ".TABLE_SETCOM ." WHERE `s_order_id`='$order_id' LIMIT 1";
		$result = tep_db_fetch_array(tep_db_query($query));
		if($result['tnxid']) {
			$payment_data = array('tnxid'=>$result['tnxid'],'checksum'=>$result['checksum'],'parity'=>$result['parity']);
			$this->before_process(true,$payment_data,$result['s_order_id'],$result['s_setcom_id']);
			return true;
		}
		return false;
	}
	
	/**
	 * Returns count of all payments made with Setcom module
	 *
	 * @return integer
	 */
	function get_all_count() {
		global $db;
		if(!isset($this->totalCount)) {
			$query = "select count(s_order_id) as count from ".TABLE_SETCOM;
			$result = tep_db_fetch_array(tep_db_query($query));
			$this->totalCount = $result['count'];	
		}
		return $this->totalCount;
	}
	
	/**
	 * Returns all fields for given payment Id
	 *
	 * @param integer $id
	 * @return array
	 */
	function get_payment_details($id) {
		global $db;
		$result = array();
		$query = "select * from ".TABLE_SETCOM ." where `s_order_id`='$id'";
		$query_result = tep_db_query($query);
		while (!$query_result->EOF) {
			$result = $query_result->fields;
			$query_result->MoveNext();
		}
		return $result;
	}
	
	// next two funcions are needed, because there are 2 different Objects of messageStack class, depending if it's called
	// by admin or by oridnary user, they have same function names, but accept different params and have different returns
	
	/**
	 * Adds error to message stack. Used as proxy to messageStack classes (there are 2 in Zen-Cart)
	 *
	 * @param string $message
	 * @param string $type
	 */
	function _add_error($message,$type) {
		global $messageStack;
		if(is_a($messageStack,'tableBlock')) {
			$messageStack->add ( $message, $type );
		} else {
			$messageStack->add_session ( 'checkout_payment', $message, $type );
		}		
	}
	
	/**
	 * Outputs all added erros as string. Used as proxy to messageStack classes (there are 2 in Zen-Cart)
	 *
	 * @return string
	 */
	function _render_errors() {
		global $messageStack;
		if(is_a($messageStack,'tableBlock')) {
			return $messageStack->output();
		} else {
			ob_start();
				$messageStack->output('checkout_payment');
			return ob_get_clean();
		}
	}
	
}

/**
 * Class used to facitlitate easy access to DOMXML data for PHP4 
 *
 * @author Piotr Nastaly
 */
class EasyDOMXMLAccess {
	
	/**
	 * Sets value as a child
	 *
	 * @param string $name Name of the value
	 * @param mixed $value
	 */
	function set($name,$value) {
		if(isset($this->$name)) {
			if(is_array($this->$name)) {
				array_push($this->$name,$value);
			} else {
				$tmp = array();
				array_push($tmp,$this->$name);
				array_push($tmp,$value);
				$this->$name = $tmp;
			}
		} else {
			$this->$name = $value;
		}
		$this->names[] = $name;
	}

	/**
	 * Sets object as a child
	 *
	 * @param string $name Desired name of the object
	 * @param EasyDOMXMLAccess $object
	 */
	function setObject($name,&$object) {
		if(isset($this->$name)) {
			if(is_array($this->$name)) {
				array_push($this->$name,$object);
			} else {
				$tmp = array();
				array_push($tmp,$this->$name);
				array_push($tmp,$object);
				$this->$name = $tmp;
			}
		} else {
			$this->$name = $object;
		}
		$this->names[] = $name;
	}

	/**
	 * Sets attribute for this node
	 *
	 * @param string $name
	 * @param mixed $value
	 */
	function setAttribute($name,$value) {
		$this->attributes[$name]=$value;
	}

	/**
	 * Return attributes of this node as associative array
	 *
	 * @return array
	 */
	function attributes() {
		return $this->attributes;
	}

	/**
	 * Return all children of tis node as an array
	 *
	 * @return array
	 */
	function children() {
		$children = array();
		foreach($this->names as $name) {
			if(is_array($this->$name)) {
				$children = array_merge($this->$name,$children);
			} else {
				$children[$name] = $this->$name;
			}
		}
		return $children;
	}
	
	/**
	 * Parses DOMXML node into EasyDOMXMLAccess objects tree
	 *
	 * @param DOMXMLNode $node Node to parse
	 * @param EasyDOMXMLAccess $root_object Root object to attach to
	 */
	function parse_node($node, &$root_object) {
		if ($node->has_attributes ()) {
			foreach ( $node->attributes () as $attribute ) {
				$root_object->setAttribute ( $attribute->name, $attribute->value );
			}
		}
		if ($node->has_child_nodes ()) {
			foreach ( $node->child_nodes () as $n ) {
				if ($n->has_child_nodes ()) {
					$child = $n->first_child ();
					//if it has only single,text, child node, set the value 
					if ($child->name == '#text' && count ( $n->child_nodes () ) == 1) {
						$root_object->set ( $n->node_name (), $child->node_value () );
					} else {
						//there are more nodes or not text ones, create an object (descend)
						$newRoot = new EasyDOMXMLAccess ( );
						//set teh values, before we set add it to this object (reference confusion)
						$this->parse_node ( $n, $newRoot );
						$root_object->setObject ( $n->node_name (), $newRoot );
					
					}
				
				}
			}
		}
	
	}
}

?>